package com.example.neuronview

import android.opengl.GLES20.GL_ARRAY_BUFFER
import android.opengl.GLES20.GL_BLEND
import android.opengl.GLES20.GL_COLOR_BUFFER_BIT
import android.opengl.GLES20.GL_DEPTH_TEST
import android.opengl.GLES20.GL_FLOAT
import android.opengl.GLES20.GL_LINES
import android.opengl.GLES20.GL_ONE_MINUS_SRC_ALPHA
import android.opengl.GLES20.GL_SRC_ALPHA
import android.opengl.GLES20.GL_STATIC_DRAW
import android.opengl.GLES20.GL_TRIANGLES
import android.opengl.GLES20.glBindBuffer
import android.opengl.GLES20.glBlendFunc
import android.opengl.GLES20.glBufferData
import android.opengl.GLES20.glClear
import android.opengl.GLES20.glClearColor
import android.opengl.GLES20.glDisable
import android.opengl.GLES20.glDrawArrays
import android.opengl.GLES20.glEnable
import android.opengl.GLES20.glEnableVertexAttribArray
import android.opengl.GLES20.glGenBuffers
import android.opengl.GLES20.glUniform4f
import android.opengl.GLES20.glUniformMatrix4fv
import android.opengl.GLES20.glVertexAttribPointer
import android.opengl.GLES20.glViewport
import android.opengl.GLES30.glBindVertexArray
import android.opengl.GLES30.glGenVertexArrays
import android.opengl.GLSurfaceView
import android.view.MotionEvent
import glm_.glm
import glm_.mat4x4.Mat4
import glm_.vec3.Vec3
import glm_.vec4.Vec4
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import java.nio.IntBuffer
import java.util.concurrent.Executors
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10
import kotlin.math.pow
import kotlin.math.sqrt

class NeuronRenderer(val glSurfaceView: BaseGLSurfaceView) : GLSurfaceView.Renderer {

    private lateinit var mMesh: Mesh
    private var mProjection: Mat4 = Mat4(1)
    private var vertexBufferP = 0
    private var vertexArrayBufferP = 0
    private var m_width = 0
    private var m_height = 0
    private var mProgram = Program()
    private var mNeuronProgram = Program()
    private var m_Trackball = Trackball()
    private var m_PreDist: Float = 0.0f
    private var m_scale: Float = 1.0f
    private var m_NeuronsGeo: MutableMap<String, Geometry> = mutableMapOf()

    // 添加线程池用于后台任务处理
    private val backgroundExecutor = Executors.newSingleThreadExecutor()
    fun setMesh(mesh: Mesh) {
        mMesh = mesh
    }

    override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f)
        val vao = IntBuffer.allocate(1)
        glGenVertexArrays(1, vao)
        vertexArrayBufferP = vao[0]
        glBindVertexArray(vao[0])
        // 创建顶点缓冲区对象
        val buffers = IntArray(1)
        glGenBuffers(1, buffers, 0)
        vertexBufferP = buffers[0]

        // 将顶点数据上传到缓冲区对象
        val vertexarray = mMesh.toFloatArray()
        mMesh.vertices = List<Vec3>(0) { Vec3(0) }
        mMesh.normals = List<Vec3>(0) { Vec3(0) }
        mMesh.faces =
            List<Face>(0) { Face(List<Int>(0) { 0 }, List<Int>(0) { 0 }, List<Int>(0) { 0 }) }
        val vertexBuffer = ByteBuffer.allocateDirect(vertexarray.size * 4)
            .order(ByteOrder.nativeOrder())
            .asFloatBuffer()
        vertexBuffer.put(vertexarray)
        vertexBuffer.position(0)

        glBindBuffer(GL_ARRAY_BUFFER, vertexBufferP)
        glBufferData(
            GL_ARRAY_BUFFER,
            vertexarray.size * 4,
            vertexBuffer,
            GL_STATIC_DRAW
        )

        glBindBuffer(GL_ARRAY_BUFFER, 0)

        // 加载并创建着色器程序
        mProgram.CreateProgram()
        mNeuronProgram.CreateProgram(vertexNeuronShaderCode, fragmentNeuronShaderCode)

    }

    override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
        m_width = width
        m_height = height
        glViewport(0, 0, width, height)
        val projection =
            glm.perspective(glm.radians(45.0), m_width / m_height.toDouble(), 0.1, 1000.0)
        mProjection = Mat4(projection)
        m_Trackball.setWindowSize(width, height)
        m_Trackball.setRadius(1.9f)
    }

    private fun Mat4toFloatBuffer(mat: Mat4): FloatBuffer {
        val floatArray = mat.toFloatArray() // 转换为FloatArray（16个元素）
        // 包装为直接缓冲区（避免JVM堆内存拷贝）
        val modelBuffer = ByteBuffer.allocateDirect(floatArray.size * 4)
            .order(ByteOrder.nativeOrder()) // 匹配底层平台字节序
            .asFloatBuffer()
            .put(floatArray)
        modelBuffer.flip() // 切换为读模式
        return modelBuffer
    }

    override fun onDrawFrame(gl: GL10?) {
        glClear(GL_COLOR_BUFFER_BIT)
        mProgram.Use()
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glDisable(GL_DEPTH_TEST);

        // 绑定顶点缓冲区对象并启用顶点属性
        glBindVertexArray(vertexArrayBufferP)
        glBindBuffer(GL_ARRAY_BUFFER, vertexBufferP)

        val positionloc: Int = mProgram.GetAttribLocation("vPosition")
        glEnableVertexAttribArray(positionloc)
        glVertexAttribPointer(positionloc, 3, GL_FLOAT, false, 6 * 4, 0)
        val normalloc: Int = mProgram.GetAttribLocation("normal")
        glEnableVertexAttribArray(normalloc)
        glVertexAttribPointer(normalloc, 3, GL_FLOAT, false, 6 * 4, 12)


        // 设置片段着色器的颜色
        val colorLocation =
            mProgram.GetUniformLocation("vColor")//glGetUniformLocation(mProgram.GetProgram(), "vColor")
        glUniform4f(colorLocation, 1.0f, 1.0f, 1.0f, 1.0f)
        m_Trackball.getRotationMatrix()
        val center = (mMesh.minVertex + mMesh.maxVertex) / 20000.0
        val mat1 = Mat4(1)
        val mat2 = glm.rotate(Mat4(1), glm.radians(180.0f), Vec3(1, 0, 0))

        val modelMatrix = glm.translate(mat1, center) * m_Trackball.getRotationMatrix() * mat2 *
                glm.translate(mat1, -center)

//        val center = (mMesh.minVertex+mMesh.maxVertex)/20000.0f
        val eye = Vec3(
            center.x,
            center.y,
            center.z + (mMesh.minVertex - mMesh.maxVertex).length() / 10000.0f * m_scale
        )
        val up = Vec3(0.0, 1.0, 0.0)
        val viewMatrix = glm.lookAt(eye, center, up)

        val modelLoc = mProgram.GetUniformLocation("model");
        val viewLoc = mProgram.GetUniformLocation("view");
        val projLoc = mProgram.GetUniformLocation("projection");
        glUniformMatrix4fv(modelLoc, 1, false, Mat4toFloatBuffer(modelMatrix))
        glUniformMatrix4fv(viewLoc, 1, false, Mat4toFloatBuffer(viewMatrix))
        glUniformMatrix4fv(projLoc, 1, false, Mat4toFloatBuffer(mProjection))

        // 绘制三角形
        glDrawArrays(GL_TRIANGLES, 0, 295914) // GL_TRIANGLES:三角形 GL_POINTS:点

        // 禁用顶点属性并解除顶点缓冲区对象的绑定

        m_NeuronsGeo.forEach { path, geometry ->
            mNeuronProgram.Use()
            glBindVertexArray(geometry.m_VAO)
            glBindBuffer(GL_ARRAY_BUFFER, geometry.m_VBO)

            val modelLoc = mNeuronProgram.GetUniformLocation("model");
            val viewLoc = mNeuronProgram.GetUniformLocation("view");
            val projLoc = mNeuronProgram.GetUniformLocation("projection");
            glUniformMatrix4fv(modelLoc, 1, false, Mat4toFloatBuffer(modelMatrix))

            glUniformMatrix4fv(viewLoc, 1, false, Mat4toFloatBuffer(viewMatrix))
            glUniformMatrix4fv(projLoc, 1, false, Mat4toFloatBuffer(mProjection))
            val colorLocation =
                mNeuronProgram.GetUniformLocation("vColor")//glGetUniformLocation(mProgram.GetProgram(), "vColor")
            glUniform4f(
                colorLocation,
                geometry.m_Color._x,
                geometry.m_Color._y,
                geometry.m_Color._z,
                geometry.m_Color._w
            )
            val position = geometry.mVertex.getProperty(Type.Vertex) as PropertyType<Float>
            glDrawArrays(GL_LINES, 0, position.size() / 3)
        }
    }

    fun onTouchEvent(event: MotionEvent): Boolean {
        var x = event.x
        var y = event.y
        println(event.pointerCount)
        if (event.pointerCount == 2) {
            val p0: MotionEvent.PointerCoords = MotionEvent.PointerCoords()
            event.getPointerCoords(0, p0)
            val p1: MotionEvent.PointerCoords = MotionEvent.PointerCoords()
            event.getPointerCoords(1, p1)
            val dist = sqrt(
                (p0.x.toDouble() - p1.x.toDouble()).pow(2.0.toDouble()) + (p0.y.toDouble() - p1.y.toDouble()).pow(
                    2.0.toDouble()
                )
            )

            when (event.actionMasked) {
                MotionEvent.ACTION_POINTER_DOWN -> {
                    m_PreDist = dist.toFloat()
                    println("DOWN$dist,$m_PreDist,$m_scale")
                }

                MotionEvent.ACTION_MOVE -> {
                    m_scale =
                        (if (dist - m_PreDist > 0) m_scale * 0.99 else m_scale * 1.01).toFloat()
                    m_scale = (if (m_scale < 0.1) 0.1 else m_scale.toDouble()).toFloat()
                    m_scale = if (m_scale > 1) 1f else m_scale
                    println("MOVE$dist,$m_PreDist,$m_scale")
                    m_Trackball.setDragging(false)
                }
            }
        } else if (event.action == MotionEvent.ACTION_DOWN) {
            println("press")
            m_Trackball.startDrag(x.toInt(), y.toInt())
        } else if (event.action == MotionEvent.ACTION_UP) {
            println("up")

        } else if (event.action == MotionEvent.ACTION_MOVE) {
            println("motion{$x,$y}")
            m_Trackball.drag(x.toInt(), y.toInt())
        }
        glSurfaceView.requestRender()
        return true
    }

    fun removeNeuron(neuron: Neuron) {
        glSurfaceView.queueEvent {

            synchronized(m_NeuronsGeo) {
                if (m_NeuronsGeo.containsKey(neuron.filePath)) {
                    m_NeuronsGeo.remove(neuron.filePath)
                }
            }

        }
        glSurfaceView.requestRender()

    }

    fun addNeuron(neuron: Neuron, color: Int) {
        // 1. 先在后台线程执行耗时的文件读取操作
        backgroundExecutor.execute {


            // 耗时操作：在后台线程读取文件
            val tree = NeuronTree()
            val geometry = try {
                tree.readFile(neuron.filePath)
            } catch (e: Exception) {
                null
            } ?: return@execute

            glSurfaceView.queueEvent {
                val alpha = (color shr 24) and 0xFF  // 获取 alpha 通道（0-255）
                val red = (color shr 16) and 0xFF    // 获取红色通道（0-255）
                val green = (color shr 8) and 0xFF   // 获取绿色通道（0-255）
                val blue = color and 0xFF            // 获取蓝色通道（0-255）
                val c = Vec4(
                    red.toFloat() / 255.0,
                    green.toFloat() / 255.0,
                    blue.toFloat() / 255.0,
                    1.0
                )

                val vao = IntBuffer.allocate(1)
                val vbo = IntBuffer.allocate(1)

//             切换到神经元着色器程序
                mNeuronProgram.Use()

                // 创建VAO
                glGenVertexArrays(1, vao)
                glBindVertexArray(vao[0])

                // 创建VBO并上传数据
                glGenBuffers(1, vbo)
                glBindBuffer(GL_ARRAY_BUFFER, vbo[0])

                // 获取顶点数据
                val position = geometry.mVertex.getProperty(Type.Vertex) as? PropertyType<Float>
                    ?: return@queueEvent
                val positionArray = position.mData.toFloatArray()
                val vertexBuffer = ByteBuffer.allocateDirect(positionArray.size * 4)
                    .order(ByteOrder.nativeOrder())
                    .asFloatBuffer()
                    .apply {
                        put(positionArray)
                        position(0)
                    }

                // 上传顶点数据到VBO
                glBufferData(GL_ARRAY_BUFFER, positionArray.size * 4, vertexBuffer, GL_STATIC_DRAW)
                val positionloc: Int = mNeuronProgram.GetAttribLocation("vPosition")
                glVertexAttribPointer(positionloc, 3, GL_FLOAT, false, 12, 0)
                glEnableVertexAttribArray(positionloc)
                // 解绑缓冲区和VAO（避免后续操作污染）
                glBindBuffer(GL_ARRAY_BUFFER, 0)
                glBindVertexArray(0)

                // 保存VAO和VBO到geometry
                geometry.m_VAO = vao[0]
                geometry.m_VBO = vbo[0]
                geometry.m_Color = c
                // 线程安全地更新共享Map
                synchronized(m_NeuronsGeo) {
                    m_NeuronsGeo[neuron.filePath] = geometry

                }
                glSurfaceView.requestRender()

            }
        }


    }

}